// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;

using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace Microsoft.TeamFoundation.Migration.Toolkit.Wit
{
    /// <summary>
    /// Collection of TFS work items.
    /// </summary>
    class TfsMigrationWorkItems: IEnumerable<IMigrationWorkItem>, IEnumerator<IMigrationWorkItem>
    {
        private const int BatchSize = 1000;                 // Number of items to process from a single query

        private TfsCore m_core;                             // TFS core
        private WorkItemStore m_store;                      // Source work item store
        private string m_queryBase;                         // Query base
        private Dictionary<string, object> m_context;       // Query context
        private WorkItemCollection m_items;                 // Collection of work items

        private TfsMigrationWorkItem m_item;                // Current item
        private int m_index;                                // Index of the current item
        private int m_lastId;                               // Id of the last processed item
        private DateTime m_asOf;                            // As of date time

        /// <summary>
        /// Return time of the first query.
        /// </summary>
        public DateTime AsOf { get { return m_asOf; } }

        /// <summary>
        /// Enumerable's constructor.
        /// </summary>
        /// <param name="core">TFS core object</param>
        /// <param name="store">Source work item store</param>
        /// <param name="condition">Query's condition for obtaining work items</param>
        public TfsMigrationWorkItems(
            TfsCore core,
            WorkItemStore store,
            string condition)
        {
            m_core = core;
            m_store = store;
            m_queryBase = string.Format(
                CultureInfo.InvariantCulture,
                "SELECT [System.Id], [System.Rev] FROM WorkItems WHERE ({0})",
                condition);
            m_context = new Dictionary<string, object>();
            m_context.Add("Project", m_core.Config.Project);
            Query(false);
        }

        /// <summary>
        /// Enumerator's constructor.
        /// </summary>
        /// <param name="src">Source enumerator</param>
        private TfsMigrationWorkItems(
            TfsMigrationWorkItems src)
        {
            m_core = src.m_core;
            m_store = src.m_store;
            m_queryBase = src.m_queryBase;
            m_context = src.m_context;
            m_items = src.m_items;
        }

        #region IEnumerable<IMigrationWorkItem> methods

        /// <summary>
        /// Creates an enumerator for the collection.
        /// </summary>
        /// <returns>Enumerator</returns>
        IEnumerator<IMigrationWorkItem> IEnumerable<IMigrationWorkItem>.GetEnumerator()
        {
            return new TfsMigrationWorkItems(this);
        }

        /// <summary>
        /// Creates an enumerator for the collection.
        /// </summary>
        /// <returns>Enumerator</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new TfsMigrationWorkItems(this);
        }

        #endregion

        #region IEnumerator<IMigrationWorkItem> Members

        public IMigrationWorkItem Current { get { return m_item; } }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            // There's nothing to dispose
        }

        #endregion

        #region IEnumerator Members

        object System.Collections.IEnumerator.Current { get { return m_item; } }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public bool MoveNext()
        {
            for (; ; )
            {
                while (m_index < BatchSize && m_index < m_items.Count)
                {
                    try
                    {
                        m_item = new TfsMigrationWorkItem(m_core, m_items[m_index++]);
                        m_lastId = m_item.WorkItem.Id;
                        return true;
                    }
                    catch (Exception e)
                    {
                        TraceManager.TraceException(e);
                    }
                }

                if (m_index == BatchSize)
                {
                    // Requery
                    Query(true);
                    continue;
                }
                return false;
            }
        }

        public void Reset()
        {
            Query(false);
        }

        #endregion

        /// <summary>
        /// Queries for work items.
        /// </summary>
        /// <param name="useLastIdFlag">Tells to use last processed id</param>
        private void Query(
            bool useLastIdFlag)
        {
            StringBuilder q = new StringBuilder(m_queryBase);
            if (useLastIdFlag)
            {
                q.AppendFormat(CultureInfo.InvariantCulture, " AND [System.Id] > {0}", m_lastId);
            }
            q.Append(" ORDER BY [System.Id]");

            string query = q.ToString();
            TraceManager.TraceInformation("TFS Query: {0}", query);
            Query qry = new Query(m_store, query, m_context, false);
            m_items = qry.EndQuery(qry.BeginQuery());
            TraceManager.TraceInformation("TFS Query: returned {0} item(s)", m_items.Count);

            if (!useLastIdFlag)
            {
                m_asOf = qry.AsOfUTC;
            }
            m_index = 0;
        }
    }
}
